package main;

import core.EventLoopGroup;
import core.Handler;
import http.HttpCodecHandler;
import http.HttpServerHandler;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class MyServer implements Runnable{
    private int port=18989;

    private final String url;

    private int timeout;

    public MyServer(int port,String url,int timeout){
        this.port=port;
        this.url=url;
        this.timeout=timeout;
    }

    public void run(int port,String url,int timeout) throws IOException {
        // 创建服务端Channel，绑定端口并配置非阻塞
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.setOption(StandardSocketOptions.SO_REUSEADDR,true);
        System.out.printf("Listening port :%d\n",port);

        // 创建多路复用器selector，并将channel注册到多路复用器上
        // 不能直接调用channel的accept方法，因为属于非阻塞，直接调用没有新连接会直接返回
        Selector selector = Selector.open();
        //SelectionKey.OP_ACCEPT —— 接收连接继续事件，表示服务器监听到了客户连接,仅服务端使用
        //SelectionKey.OP_CONNECT —— 连接就绪事件，表示客户与服务器的连接已经建立成功,仅客户端使用
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        EventLoopGroup eventLoopGroup = new EventLoopGroup();

        // 循环处理多路复用器的IO事件
        for (; ; ) {
            try {
                //选择一个通道，此方法是阻塞的，在有一个通道、线程中断或Selector的唤醒时，返回
                selector.select(1000);
            } catch (IOException e) {
                e.printStackTrace();
                break;
            }
            //获取的一个selectKey集合，该集合只能删除不能添加，
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while(iterator.hasNext()){
                SelectionKey key = iterator.next();
                iterator.remove();
                // accept事件，说明有新的客户端连接
                if (key.isAcceptable()){
                    ServerSocketChannel server = (ServerSocketChannel) key.channel();
                    SocketChannel socketChannel = server.accept();
                    socketChannel.setOption(StandardSocketOptions.SO_KEEPALIVE,true);
                    socketChannel.configureBlocking(false);
                    // 注册到专门处理读写事件的selector上
                    System.out.println("Accepted connection from "+socketChannel);
                    Handler[] handles=new Handler[]{new HttpCodecHandler(),new HttpServerHandler(url)};
                    /*
                     |SelectionKey.OP_READ|SelectionKey.OP_WRITE 客户端和服务端都会使用
                     1.当向通道中注册SelectionKey.OP_READ事件后，如果客户端有向缓存中write数据，下次轮询时，则会 isReadable()=true；
                     2.当向通道中注册SelectionKey.OP_WRITE事件后，这时你会发现当前轮询线程中isWritable()一直为ture，如果不设置为其他事件
                     */
                    eventLoopGroup.register(socketChannel,SelectionKey.OP_READ,timeout,handles);
                }

            }
        }
        selector.close();
    }

    @Override
    public void run() {
        try {
            run(this.port,this.url,this.timeout);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}
